package org.ko.core.dao.hibernate4;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.ko.commons.util.MyBeanUtils;
import org.ko.core.dao.support.Finder;
import org.ko.core.dao.support.Updater;
import org.ko.core.websupport.page.Page;
import org.springframework.util.Assert;


/**
 * hibernate DAO基类
 * 
 * 提供hql分页查询，拷贝更新等一些常用功能。
 * @param <T>
 *            entity class
 * @param <ID>
 *            entity id
 */
public abstract class BaseTypedDao<T, ID extends Serializable> extends
		BaseSimpleDao {
	
	private Class<T> entityClass;
	/**
	 * @see Session.get(Class,Serializable)
	 * @param id
	 * @return 持久化对象
	 */
	public T get(ID id) {
		return get(id, false);
	}

	/**
	 * @see Session.get(Class,Serializable,LockMode)
	 * @param id
	 *            对象ID
	 * @param lock
	 *            是否锁定，使用LockMode.UPGRADE
	 * @return 持久化对象
	 */
	@SuppressWarnings("unchecked")
	public T get(ID id, boolean lock) {
		T entity;
		if (lock) {
			entity = (T) getSession().get(getEntityClass(), id,LockOptions.UPGRADE);
		} else {
			entity = (T) getSession().get(getEntityClass(), id);
		}
		return entity;
	}
	
	@SuppressWarnings("unchecked")
	public ID save(T entity){
		Assert.notNull(entity);
		return (ID)getSession().save(entity);
	}
	
	/**
	 * 更新实体
	 */
	public Object update(Object entity) {
		Assert.notNull(entity);
		getSession().update(entity);
		return entity;
	}
	
	/**
	 * 更新实体
	 */
	public Object merge(Object entity) {
		Assert.notNull(entity);
		return getSession().merge(entity);
	}
	
	public void delete(T entity) {
		getSession().delete(entity);
	}
	
	public void delete(ID id) {
		getSession().delete(get(id));
	}
	
	public void delete(ID[] ids) {
		Assert.notNull(ids);
		for (ID id : ids) {
			delete(id);
		}
	}

	/**
	 * 根据ID删除实体
	 */
	public T deleteById(ID id) {
		Assert.notNull(id);
		T entity = get(id);
		getSession().delete(entity);
		return entity;
	}

	/**
	 * 按属性查找对象列表
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String property, Object value) {
		Assert.hasText(property);
		return createCriteria(Restrictions.eq(property, value)).list();
	}

	/**
	 * 按属性查找唯一对象
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueByProperty(String property, Object value) {
		Assert.hasText(property);
		Assert.notNull(value);
		return (T) createCriteria(Restrictions.eq(property, value))
				.uniqueResult();
	}

	/**
	 * 按属性统计记录数
	 * 
	 * @param property
	 * @param value
	 * @return
	 */
	public int countByProperty(String property, Object value) {
		Assert.hasText(property);
		Assert.notNull(value);
		return ((Number) (createCriteria(Restrictions.eq(property, value))
				.setProjection(Projections.rowCount()).uniqueResult()))
				.intValue();
	}
	
	public int countByProperty(Criterion... criterions) {
		Number ret =  ((Number) (createCriteria(criterions).setProjection(Projections.rowCount()).uniqueResult()));
		return ret==null?0:ret.intValue();
	}
	
	/**
	 * 按属性统计记录数
	 * 
	 * @param property
	 * @param value
	 * @return
	 */
	public int maxByProperty(String property) {
		Number ret = ((Number)createCriteria().setProjection(Projections.max(property)).uniqueResult());
		return null==ret?0:ret.intValue();
	}

	/**
	 * 直接用Page对象进行翻页查询
	 * @param finder
	 * @param p
	 */
	@SuppressWarnings("rawtypes")
	public void find(Finder finder, Page<T> p) {
		int totalCount = countQueryResult(finder);
		if (totalCount < 1) {
			p.setList(new ArrayList<T>());
			return ;
		}
		p.setTotalCount(totalCount);
		Query query = getSession().createQuery(finder.getOrigHql());
		finder.setParamsToQuery(query);
		query.setFirstResult(p.getFirstResult());
		query.setMaxResults(p.getPageSize());
		if (finder.isCacheable()) {
			query.setCacheable(true);
		}
		List list = query.list();
		p.setList(list);
	}
	
	/**
	 * 按Criterion查询列表数据.
	 * 
	 * @param criterion
	 *            数量可变的Criterion.
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Criterion... criterion) {
		return createCriteria(criterion).list();
	}
	
	/**
	 * 创建Criteria
	 * @return
	 */
	public Criteria createCritera(){
		return getSession().createCriteria(getEntityClass());
	}

	/**
	 * 通过Updater更新对象
	 * 
	 * @param updater
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "deprecation" })
	public T updateByUpdater(Updater<T> updater) {
		ClassMetadata cm = sessionFactory.getClassMetadata(getEntityClass());
		T bean = updater.getBean();
		//FIXME:
		T po = (T) getSession().get(getEntityClass(),
				cm.getIdentifier(bean));
		updaterCopyToPersistentObject(updater, po, cm);
		return po;
	}
	
	/**
	 * 释放实体
	 * @param entity
	 */
	public void evict(T entity){
		getSession().evict(entity);
	}
	
	/**
	 * 执行hql的更新
	 * @param hql
	 * @return
	 */
	public int runHql(String hql){
		return getSession().createQuery(hql).executeUpdate();
	}
	
	/**
	 * 执行sql的更新
	 * @param sql
	 * @return
	 */
	public int runSql(String sql){
		return getSession().createSQLQuery(sql).executeUpdate();
	}

	/**
	 * 将更新对象拷贝至实体对象，并处理many-to-one的更新。
	 * 
	 * @param updater
	 * @param po
	 */
	private void updaterCopyToPersistentObject(Updater<T> updater, T po,
			ClassMetadata cm) {
		String[] propNames = cm.getPropertyNames();
		String identifierName = cm.getIdentifierPropertyName();
		T bean = updater.getBean();
		Object value;
		for (String propName : propNames) {
			if (propName.equals(identifierName)) {
				continue;
			}
			try {
				value = MyBeanUtils.getSimpleProperty(bean, propName);
				if (!updater.isUpdate(propName, value)) {
					continue;
				}
				cm.setPropertyValue(po, propName, value);
			} catch (Exception e) {
				throw new RuntimeException(
						"copy property to persistent object failed: '"
								+ propName + "'", e);
			}
		}
	}

	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(getEntityClass());
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 获得Dao对于的实体类
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Class<T> getEntityClass(){
		if(entityClass == null){
			entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		}
		
		return entityClass;
	}
	
	/**
	 *<pre>
	 *获取真实的数据表名，在执行原生sql时用
	 *</pre>
	 * @return
	 */
	public String getTableName(){
		return TableHelper.getTableName(getEntityClass());
	}
}
